#pragma once
#include <assert.h>

namespace DirectUI
{
#define DECLARE_CLASS(className, objName)	\
public:	\
	static	LPCTSTR ClassName() { return L#objName; }	\
	static	UIObjectType ObjectType() { return ot_##objName; }	\
	static	IUIObject* CreateObject() { return new className(); }	\
	virtual	UIObjectType GetObjType() { return className::ObjectType(); }	\

	struct UI_INTERFACEMAP_ENTRY
	{
		const void* piid;		// the interface id (IID) (NULL for aggregate)
		size_t nOffset;         // offset of the interface vtable from m_unknown
	};
	struct UI_INTERFACEMAP
	{
		const UI_INTERFACEMAP*			pBaseMap;
		const UI_INTERFACEMAP_ENTRY*	pEntry;		// map for this class
	};

#define UI_DECLARE_INTERFACE_MAP() \
private: \
	static const UI_INTERFACEMAP_ENTRY _interfaceEntries[]; \
protected: \
	static const UI_INTERFACEMAP interfaceMap; \
	virtual const UI_INTERFACEMAP* GetInterfaceMap() const; \

#define UI_BEGIN_INTERFACE_MAP(theClass, theBase) \
	const UI_INTERFACEMAP* theClass::GetInterfaceMap() const \
	{ return &theClass::interfaceMap; } \
	__declspec(selectany) const UI_INTERFACEMAP theClass::interfaceMap = \
	{ &theBase::interfaceMap, &theClass::_interfaceEntries[0], }; \
	__declspec(selectany) const UI_INTERFACEMAP_ENTRY theClass::_interfaceEntries[] = \
	{ \

#define UI_INTERFACE_PART(theClass, guid, localClass) \
	{ &__uuidof(guid), offsetof(theClass, m_x##localClass) }, \

#define UI_END_INTERFACE_MAP() \
	{ NULL, (size_t)-1 } \
	}; \

#define UI_BEGIN_INTERFACE_PART(theClass, localClass, baseClass) \
	class X##localClass : public CUIAggregateObject<theClass, baseClass> \
	{ \
	public: \
	size_t m_nOffset; \
	X##localClass() \
	{	\
		m_nOffset = offsetof(theClass, m_x##localClass); \
		m_lpOuterObject = ((theClass*)((BYTE*)this - offsetof(theClass, m_x##localClass)));		\
	} \

#define UI_END_INTERFACE_PART(localClass) \
	} m_x##localClass; \
	friend class X##localClass; \

#define UI_METHOD_PROLOGUE(theClass, localClass) \
	theClass* pThis = \
	((theClass*)((BYTE*)this - offsetof(theClass, m_x##localClass))); \
	pThis; // avoid warning from compiler \

	//////////////////////////////////////////////////////////////////////////
	template <typename baseclass>
	class CUIObjectImplHelper : public baseclass
	{
	private: 
		static const	UI_INTERFACEMAP_ENTRY _interfaceEntries[];
	protected: 
		static const	UI_INTERFACEMAP interfaceMap; 
		virtual const	UI_INTERFACEMAP* GetInterfaceMap() const
		{
			return &CUIObjectImplHelper::interfaceMap;
		}
	public:
		CUIObjectImplHelper() : m_dwRef(0)
		{
		}
		virtual ~CUIObjectImplHelper()
		{

		}
		virtual ULONG			AddRef() { return InternalAddRef(); }
		virtual ULONG			Release() { return InternalRelease(); }
		virtual HRESULT			QueryInterface(const GUID& guid, void** lppObject) { return InternalQueryInterface(guid, lppObject); }
		virtual	UIObjectType	GetObjType() { return InternalGetObjType(); }
		virtual	bool			IsLess(IUIObject* lpObject) { return InternalIsLess(lpObject); }
		virtual bool			IsEqual(IUIObject* lpObject) { return InternalIsEqual(lpObject); }
		virtual	void			Copy(IUIObject* lpObject) { return InternalCopy(lpObject); }

		virtual ULONG			InternalAddRef()
		{
			// const UI_INTERFACEMAP* p = GetInterfaceMap();
			assert(GetInterfaceMap() != NULL); 
			return InterlockedIncrement(&m_dwRef);
		}
		virtual ULONG			InternalRelease()
		{
			assert(GetInterfaceMap() != NULL);
			LONG lResult = InterlockedDecrement(&m_dwRef);
			if ( lResult == 0 )
			{
				delete this;
				return 0;
			}
			return lResult;
		}
		virtual HRESULT			InternalQueryInterface(const GUID& guid, void** lppObject)
		{
			const UI_INTERFACEMAP* pMap = GetInterfaceMap();
			assert(pMap != NULL);

			do
			{
				const UI_INTERFACEMAP_ENTRY* pEntry = pMap->pEntry;
				assert(pEntry != NULL);
				while ( pEntry->nOffset != (size_t)-1 )
				{
					if ( guid == *(GUID*)pEntry->piid ) 
					{
						IUIObject* lpQuery = (IUIObject*)((BYTE*)this + pEntry->nOffset);
						*lppObject = lpQuery;
						InternalAddRef();
						return S_OK;
					}
					++pEntry;
				}
			} while ((pMap = pMap->pBaseMap) != NULL);

			return E_NOINTERFACE;
		}
		virtual	UIObjectType	InternalGetObjType()
		{
			return ot_unknow; 
		}
		virtual	bool			InternalIsLess(IUIObject* lpObject)
		{
			return (void*)this < (void*)lpObject;
		}
		virtual bool			InternalIsEqual(IUIObject* lpObject)
		{
			return (void*)this == (void*)lpObject;
		}
		virtual	void			InternalCopy(IUIObject* lpObject)
		{
			
		}
	private:
		volatile LONG m_dwRef;
	};

	template <typename baseclass>
	const UI_INTERFACEMAP CUIObjectImplHelper<baseclass>::interfaceMap =
	{
		NULL,
		&CUIObjectImplHelper<baseclass>::_interfaceEntries[0]
	};

	template <typename baseclass>
	const UI_INTERFACEMAP_ENTRY CUIObjectImplHelper<baseclass>::_interfaceEntries[] =
	{
		{ NULL, (size_t)-1 }    // end of entries
	};

	template <
		typename baseClass, 
		typename className,
		typename interfacename>
	class CUINoAggregateObjectImplHelper
		: public CUIObjectImplHelper<baseClass>
	{
	public:
		virtual HRESULT InternalQueryInterface( const GUID& guid, void** lppObject ) 
		{
			if ( guid == __uuidof(interfacename) )
			{
				if (lppObject)
				{
					*lppObject = (interfacename*)this;
					AddRef();
				}
				return S_OK;
			}

			return __super::InternalQueryInterface(guid, lppObject);
		}
		virtual	UIObjectType	GetObjType() { return className::ObjectType(); }
	protected:	
		className* GetPtr(IUIObject* lpObject)
		{
			CUIObjectQIPtr<interfacename> sp(lpObject);
			if ( !sp ) return NULL;
			return (className*)(interfacename*)sp;
		}
	};

	template <typename theclass, typename baseclass>
	class CUIAggregateObject : public baseclass
	{
	public:
		virtual ULONG			AddRef() { return m_lpOuterObject->InternalAddRef(); }
		virtual ULONG			Release() { return m_lpOuterObject->InternalRelease(); }
		virtual HRESULT			QueryInterface(const GUID& guid, void** lppObject) { return m_lpOuterObject->InternalQueryInterface(guid, lppObject); }
		virtual	UIObjectType	GetObjType() { return m_lpOuterObject->InternalGetObjType(); }
		virtual	bool			IsLess(IUIObject* lpObject) { return m_lpOuterObject->InternalIsLess(lpObject); }
		virtual bool			IsEqual(IUIObject* lpObject) { return m_lpOuterObject->InternalIsEqual(lpObject); }
		virtual	void			Copy(IUIObject* lpObject) { return m_lpOuterObject->InternalCopy(lpObject); }
	protected:
		theclass*	m_lpOuterObject;
	};

	class CUIObject : public CUIObjectImplHelper<IUIObject>
	{

	};
}

	
